﻿using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace Devonline.AspNetCore;

/// <summary>
/// 基于 HttpClient 的接口服务基类
/// </summary>
/// <typeparam name="TEndpoint"></typeparam>
/// <param name="logger"></param>
/// <param name="httpClientFactory"></param>
/// <param name="endpoint"></param>
public class HttpClientService<TEndpoint>(
    ILogger<HttpClientService<TEndpoint>> logger,
    IHttpClientFactory httpClientFactory,
    TEndpoint endpoint)
    where TEndpoint : IEndpoint
{
    protected readonly ILogger<HttpClientService<TEndpoint>> _logger = logger;
    protected readonly IHttpClientFactory _httpClientFactory = httpClientFactory;
    protected readonly TEndpoint _endpoint = endpoint;
    protected readonly string _userAccess = $"In access index {KeyGenerator.GetStringKey()}";
    protected const int TIMEOUT_SECONDS = 30;

    /// <summary>
    /// 使用 HTTP GET 方法调用接口, 适应于直接返回 response.Content 内容的形式以及无返回值的情况
    /// </summary>
    /// <param name="path">接口地址, 不传值默认使用当前 endpoint 的 Path 值</param>
    /// <param name="desc">接口描述, 不传值默认使用当前 endpoint 的 Doc 值</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public async Task<string> GetAsync(string? path = default, string? desc = default)
    {
        path ??= _endpoint.Path;
        desc ??= _endpoint.Doc;
        _logger.LogInformation($"{_userAccess}: will call interface {desc}, the api path: {path}");
        using var httpClient = _httpClientFactory.CreateClient(_endpoint.Name);
        httpClient.Timeout = TimeSpan.FromSeconds(_endpoint.Timeout ?? TIMEOUT_SECONDS);
        using var response = await httpClient.GetAsync(path);
        var result = await response.Content.ReadAsStringAsync();
        _logger.LogInformation($"{_userAccess}: call interface {desc} return data: " + result);
        if (!response.IsSuccessStatusCode)
        {
            _logger.LogWarning($"{_userAccess}: call interface {desc} failed, HTTP Status Code: {response.StatusCode}, the fail detail information please refer to api interface return data!");
            throw new BadHttpRequestException($"{_userAccess}: call interface {desc} failed, the detail information: {result}");
        }

        return result;
    }
    /// <summary>
    /// 使用 HTTP Post 方法和指定参数调用接口, 适应于直接返回 response.Content 内容的形式以及无返回值的情况
    /// </summary>
    /// <typeparam name="TRequestModel">请求参数类型</typeparam>
    /// <param name="requestModel">请求参数</param>
    /// <param name="path">接口地址, 不传值默认使用当前 endpoint 的 Path 值</param>
    /// <param name="desc">接口描述, 不传值默认使用当前 endpoint 的 Doc 值</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public async Task<string> PostAsync<TRequestModel>([DisallowNull] TRequestModel requestModel, string? path = default, string? desc = default)
    {
        path ??= _endpoint.Path;
        desc ??= _endpoint.Doc;
        var content = requestModel.ToJsonString();
        _logger.LogInformation($"{_userAccess}: will call interface {desc}, the api path: {path}, the arguments: {content}");
        using var httpContent = new StringContent(content, System.Text.Encoding.UTF8, ContentType.Json);
        using var httpClient = _httpClientFactory.CreateClient(_endpoint.Name);
        httpClient.Timeout = TimeSpan.FromSeconds(_endpoint.Timeout ?? TIMEOUT_SECONDS);
        using var response = await httpClient.PostAsync(path, httpContent);
        var result = await response.Content.ReadAsStringAsync();
        _logger.LogInformation($"{_userAccess}: call interface {desc} return data: " + result);
        if (!response.IsSuccessStatusCode)
        {
            _logger.LogWarning($"{_userAccess}: call interface {desc} failed, HTTP Status Code: {response.StatusCode}, the fail detail information please refer to api interface return data!");
            throw new BadHttpRequestException($"{_userAccess}: call interface {desc} failed, the detail information: {result}");
        }

        return result;
    }
    /// <summary>
    /// 使用 HTTP PUT 方法和指定参数调用接口, 适应于直接返回 response.Content 内容的形式以及无返回值的情况
    /// </summary>
    /// <typeparam name="TRequestModel">请求参数类型</typeparam>
    /// <param name="requestModel">请求参数</param>
    /// <param name="path">接口地址, 不传值默认使用当前 endpoint 的 Path 值</param>
    /// <param name="desc">接口描述, 不传值默认使用当前 endpoint 的 Doc 值</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public async Task<string> PutAsync<TRequestModel>([DisallowNull] TRequestModel requestModel, string? path = default, string? desc = default)
    {
        path ??= _endpoint.Path;
        desc ??= _endpoint.Doc;
        var content = requestModel.ToJsonString();
        _logger.LogInformation($"{_userAccess}: will call interface {desc}, the api path: {path}, the arguments: {content}");
        using var httpContent = new StringContent(content, System.Text.Encoding.UTF8, ContentType.Json);
        using var httpClient = _httpClientFactory.CreateClient(_endpoint.Name);
        httpClient.Timeout = TimeSpan.FromSeconds(_endpoint.Timeout ?? TIMEOUT_SECONDS);
        using var response = await httpClient.PutAsync(path ?? path, httpContent);
        var result = await response.Content.ReadAsStringAsync();
        _logger.LogInformation($"{_userAccess}: call interface {desc} return data: " + result);
        if (!response.IsSuccessStatusCode)
        {
            _logger.LogWarning($"{_userAccess}: call interface {desc} failed, HTTP Status Code: {response.StatusCode}, the fail detail information please refer to api interface return data!");
            throw new BadHttpRequestException($"{_userAccess}: call interface {desc} failed, the detail information: {result}");
        }

        return result;
    }
    /// <summary>
    /// 使用 HTTP DELETE 方法调用接口, 适应于直接返回 response.Content 内容的形式以及无返回值的情况
    /// </summary>
    /// <param name="path">接口地址, 不传值默认使用当前 endpoint 的 Path 值</param>
    /// <param name="desc">接口描述, 不传值默认使用当前 endpoint 的 Doc 值</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public async Task<string> DeleteAsync(string? path = default, string? desc = default)
    {
        path ??= _endpoint.Path;
        desc ??= _endpoint.Doc;
        _logger.LogInformation($"{_userAccess}: will call interface {desc}, the api path: {path}");
        using var httpClient = _httpClientFactory.CreateClient(_endpoint.Name);
        httpClient.Timeout = TimeSpan.FromSeconds(_endpoint.Timeout ?? TIMEOUT_SECONDS);
        using var response = await httpClient.DeleteAsync(path);
        var result = await response.Content.ReadAsStringAsync();
        _logger.LogInformation($"{_userAccess}: call interface {desc} return data: " + result);
        if (!response.IsSuccessStatusCode)
        {
            _logger.LogWarning($"{_userAccess}: call interface {desc} failed, HTTP Status Code: {response.StatusCode}, the fail detail information please refer to api interface return data!");
            throw new BadHttpRequestException($"{_userAccess}: call interface {desc} failed, the detail information: {result}");
        }

        return result;
    }

    /// <summary>
    /// 使用 HTTP GET 方法调用接口, 并返回指定类型的结果
    /// </summary>
    /// <typeparam name="TResponseModel">返回值参数类型</typeparam>
    /// <param name="path">接口地址, 不传值默认使用当前 endpoint 的 Path 值</param>
    /// <param name="desc">接口描述, 不传值默认使用当前 endpoint 的 Doc 值</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public async Task<TResponseModel?> GetAsync<TResponseModel>(string? path = default, string? desc = default)
    {
        var result = await GetAsync(path, desc);
        return ResponseAdapter<TResponseModel>(result, path, desc);
    }
    /// <summary>
    /// 使用 HTTP Post 方法和指定参数调用接口, 并返回指定类型的结果
    /// </summary>
    /// <typeparam name="TRequestModel">请求参数类型</typeparam>
    /// <typeparam name="TResponseModel">返回值参数类型</typeparam>
    /// <param name="requestModel">请求参数</param>
    /// <param name="path">接口地址, 不传值默认使用当前 endpoint 的 Path 值</param>
    /// <param name="desc">接口描述, 不传值默认使用当前 endpoint 的 Doc 值</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public async Task<TResponseModel?> PostAsync<TRequestModel, TResponseModel>([DisallowNull] TRequestModel requestModel, string? path = default, string? desc = default)
    {
        var result = await PostAsync<TRequestModel>(requestModel, path, desc);
        return ResponseAdapter<TResponseModel>(result, path, desc);
    }
    /// <summary>
    /// 使用 HTTP PUT 方法和指定参数调用接口, 并返回指定类型的结果
    /// </summary>
    /// <typeparam name="TRequestModel">请求参数类型</typeparam>
    /// <typeparam name="TResponseModel">返回值参数类型</typeparam>
    /// <param name="requestModel">请求参数</param>
    /// <param name="path">接口地址, 不传值默认使用当前 endpoint 的 Path 值</param>
    /// <param name="desc">接口描述, 不传值默认使用当前 endpoint 的 Doc 值</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public async Task<TResponseModel?> PutAsync<TRequestModel, TResponseModel>([DisallowNull] TRequestModel requestModel, string? path = default, string? desc = default)
    {
        var result = await PutAsync<TRequestModel>(requestModel, path, desc);
        return ResponseAdapter<TResponseModel>(result, path, desc);
    }
    /// <summary>
    /// 使用 HTTP DELETE 方法调用接口, 并返回指定类型的结果
    /// </summary>
    /// <typeparam name="TResponseModel">返回值参数类型</typeparam>
    /// <param name="path">接口地址, 不传值默认使用当前 endpoint 的 Path 值</param>
    /// <param name="desc">接口描述, 不传值默认使用当前 endpoint 的 Doc 值</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public async Task<TResponseModel?> DeleteAsync<TResponseModel>(string? path = default, string? desc = default)
    {
        var result = await DeleteAsync(path, desc);
        return ResponseAdapter<TResponseModel>(result, path, desc);
    }

    /// <summary>
    /// 使用 HTTP 方法调用接口后处理返回值
    /// </summary>
    /// <typeparam name="TResponseModel">HTTP 返回值类型</typeparam>
    /// <param name="content">HTTP 返回值原内容</param>
    /// <param name="path">接口地址, 不传值默认使用当前 endpoint 的 Path 值</param>
    /// <param name="desc">接口描述, 不传值默认使用当前 endpoint 的 Doc 值</param>
    /// <returns></returns>
    protected virtual TResponseModel? ResponseAdapter<TResponseModel>([DisallowNull] string content, string? path = default, string? desc = default)
    {
        if (string.IsNullOrWhiteSpace(content))
        {
            _logger.LogInformation($"{_userAccess}: call interface {desc} success, but not return data, now will back to the call point");
            return default;
        }

        var responseModel = content.ToJsonObject<TResponseModel>();
        if (responseModel is null)
        {
            _logger.LogWarning($"{_userAccess}: call interface {desc} success, but return data can not serialized, please check the return data!");
            throw new BadHttpRequestException($"调用 {desc} 接口失败, 失败原因: 返回结果无法反序列化!");
        }

        _logger.LogInformation($"{_userAccess}: call interface {desc} success, and return data serialize success, now will back the result to the call point");
        return responseModel;
    }
}